use std::collections::HashMap;

use model_graph_types::{
    connector::{Connector, ExecuteOptions, QueryOptions},
    database::{DataSource, DatabaseType},
    modeling::Value,
};

use model_graph_common::transport::http::http_post;

pub struct JdbcConnector {
    url: String,
    data_source: DataSource,
}

impl JdbcConnector {
    pub async fn open(data_source: &DataSource) -> anyhow::Result<JdbcConnector> {
        //
        Ok(Self {
            url: String::from("http://localhost:8080/sql/execute"),
            data_source: data_source.clone(),
        })
    }
}

fn get_request(
    data_source: &DataSource,
    sql: &String,
    args: &Vec<Value>,
) -> anyhow::Result<Vec<u8>> {
    let args_value: Vec<serde_json::Value> = args
        .iter()
        .map(|v| {
            let json_value = v.try_into();
            match json_value {
                Ok(value) => value,
                Err(_) => serde_json::Value::Null,
            }
        })
        .collect();

    let request = serde_json::json!({
        "dataSource":{
            "type": "mysql",
            "url":  data_source.url,
            "driver":"com.mysql.cj.jdbc.Driver",
            "username":  data_source.username,
            "password":  data_source.password,
        },
        "sql": sql,
        "args": args_value
    });
    tracing::debug!("发送参数:{}", request.to_string());
    Ok(request.to_string().into_bytes())
}

fn get_response(body: Vec<u8>) -> anyhow::Result<(Vec<Value>, Value)> {
    //message,code,data
    if let serde_json::Value::Object(response) =
        serde_json::from_slice(&body).map_err(|err| anyhow::anyhow!("json解析失败:{}", err))?
    {
        if let serde_json::Value::Number(code) = response
            .get("code")
            .ok_or(anyhow::anyhow!("没有找到code:{:?}", response))?
        {
            if code
                .as_i64()
                .ok_or(anyhow::anyhow!("code不是整数:{}", code))?
                == 0
            {
                //解析data
                match response
                    .get("data")
                    .ok_or(anyhow::anyhow!("没有找到data"))?
                {
                    serde_json::Value::Object(obj) => {
                        //metaData data
                        let meta_data: Vec<Value> = if let serde_json::Value::Array(metadatas) = obj
                            .get("metaData")
                            .ok_or(anyhow::anyhow!("没有找到metaData"))?
                        {
                            metadatas
                                .iter()
                                .map(|v| v.clone().try_into().map_or(Value::None, |o| o))
                                .filter(|v| match v {
                                    Value::None => false,
                                    _ => true,
                                })
                                .collect::<Vec<Value>>()
                        } else {
                            vec![]
                        };
                        let data_value = obj
                            .get("data")
                            .ok_or(anyhow::anyhow!("没有找到data.data"))?;

                        let data = match data_value {
                            serde_json::Value::Array(list) => Value::Array(
                                list.iter()
                                    .map(|v| v.clone().try_into().map_or(Value::None, |o| o))
                                    .filter(|v| match v {
                                        Value::None => false,
                                        _ => true,
                                    })
                                    .collect::<Vec<Value>>(),
                            ),
                            _ => data_value.clone().try_into().map_or(Value::None, |o| o),
                        };
                        Ok((meta_data, data))
                    }
                    serde_json::Value::Number(count) => Ok((
                        vec![],
                        serde_json::Value::Number(count.clone())
                            .try_into()
                            .map_or(Value::None, |o| o),
                    )),
                    _ => Ok((vec![], Value::None)),
                }
            } else {
                Err(anyhow::anyhow!("{:?}", response))
            }
        } else {
            Err(anyhow::anyhow!("没有找到code:{:?}", response))
        }
    } else {
        Err(anyhow::anyhow!("返回格式不是对象!"))
    }
}

#[async_trait::async_trait]
impl Connector for JdbcConnector {
    async fn query(
        &self,
        sql: &String,
        args: &Vec<Value>,
        options: QueryOptions,
    ) -> anyhow::Result<(Vec<Value>, Vec<Value>)> {
        let mut headers = HashMap::new();
        headers.insert(
            String::from("Content-Type"),
            String::from("application/json"),
        );
        let (_, _, res) = http_post(
            &self.url,
            &headers,
            get_request(&self.data_source, sql, args)?,
        )
        .await?;
        let (meta_data, rows) = get_response(res)?;

        let data = if let Value::Array(v) = rows {
            v
        } else {
            vec![rows]
        };
        Ok((meta_data, data))
    }

    async fn query_one(
        &self,
        sql: &String,
        args: &Vec<Value>,
        options: QueryOptions,
    ) -> anyhow::Result<(Vec<Value>, Value)> {
        let mut headers = HashMap::new();
        headers.insert(
            String::from("Content-Type"),
            String::from("application/json"),
        );
        let (_, _, res) = http_post(
            &self.url,
            &headers,
            get_request(&self.data_source, sql, args)?,
        )
        .await?;
        let (meta_data, rows) = get_response(res)?;

        let data = if let Value::Array(mut v) = rows {
            v.pop().ok_or(anyhow::anyhow!(""))?
        } else {
            rows
        };
        Ok((meta_data, data))
    }

    async fn query_map(
        &self,
        sql: &String,
        args: &Vec<Value>,
        options: QueryOptions,
    ) -> anyhow::Result<HashMap<String, Value>> {
        if let (headers, Value::Array(data)) = self.query_one(sql, args, options).await? {
            Ok(_to_object(&headers, data)?)
        } else {
            Err(anyhow::anyhow!("查询失败"))
        }
    }

    async fn query_maps(
        &self,
        sql: &String,
        args: &Vec<Value>,
        options: QueryOptions,
    ) -> anyhow::Result<Vec<HashMap<String, Value>>> {
        if let (headers, data) = self.query(sql, args, options).await? {
            let mut res = vec![];
            for row in data {
                match row {
                    Value::Array(v) => res.push(_to_object(&headers, v)?),
                    _ => {}
                }
            }
            Ok(res)
        } else {
            Err(anyhow::anyhow!("查询失败"))
        }
    }

    async fn execute(
        &self,
        sql: &String,
        args: &Vec<Value>,
        options: ExecuteOptions,
    ) -> anyhow::Result<i64> {
        let mut headers = HashMap::new();
        headers.insert(
            String::from("Content-Type"),
            String::from("application/json"),
        );
        let res = http_post(
            &self.url,
            &headers,
            get_request(&self.data_source, sql, args)?,
        )
        .await?;

        Ok(0)
    }

    async fn close(&mut self) -> anyhow::Result<i32> {
        Ok(0)
    }
}

fn _to_object(headers: &Vec<Value>, data: Vec<Value>) -> anyhow::Result<HashMap<String, Value>> {
    let mut object = HashMap::new();

    for i in 0..headers.len() {
        if let Some(header) = headers.get(i) {
            match header {
                Value::Object(hm) => {
                    if let Some(name) = hm.get(&String::from("name")) {
                        match name {
                            Value::String(hv) => {
                                if let Some(value) = data.get(i) {
                                    object.insert(hv.clone(), value.clone());
                                }
                            }
                            _ => {}
                        }
                    }
                }
                Value::String(hv) => {
                    if let Some(value) = data.get(i) {
                        object.insert(hv.clone(), value.clone());
                    }
                }
                _ => {}
            }
        }
    }
    Ok(object)
}
